package com.ipzoe.common.freeMarker;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ipzoe.common.util.StringUtil;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

/**
 * 自动生成实体类客户端
 * <p>
 * Created 2018/01/19.
 *
 * @author pfzhang
 */
public class EntityGeneratorClient {

    private static File javaFile = null;

    public static void main(String[] args) {
        List<Property> propertyList = Lists.newArrayList();
        //实体类包名
        String entityJavaPackage = "com.ipzoe.token75.bean.entity";
        //Repository类包名
        String repositoryJavaPackage = "com.ipzoe.token75.repository";
        //Service类包名
        String serviceJavaPackage = "com.ipzoe.token75.service";

        //实体类名(驼峰)
        String className = "Complain";
        //实体类描述
        String describe = "申诉";

        //设置实体类属性
        createProperty(PropertyType.Long, "orderId", "订单ID", propertyList);
        createProperty(PropertyType.Long, "accountId", "用户ID", propertyList);
        createProperty(PropertyType.Integer, "status", "状态[1=申诉中,2=成功,3=驳回,4=取消]", propertyList);
        createProperty(PropertyType.String, "evidence", "凭证", propertyList);

        //生成实体类/Repository/Service/sql/mapper
        generate(entityJavaPackage, className, describe, propertyList, repositoryJavaPackage, serviceJavaPackage);
    }

    /**
     * 生成实体类/Repository/Service/sql/mapper
     *
     * @param entityJavaPackage     实体类包名
     * @param className             实体类名(驼峰)
     * @param describe              实体类描述
     * @param propertyList          属性集合
     * @param repositoryJavaPackage Repository包名
     * @param serviceJavaPackage    Service包名
     */
    private static void generate(String entityJavaPackage, String className, String describe,
                                 List<Property> propertyList, String repositoryJavaPackage, String serviceJavaPackage) {
        Map<String, Object> root = createDataModel(entityJavaPackage, className, describe, propertyList);
        create(root, "entity.ftl");
        root = createRepositoryModel(root, repositoryJavaPackage);
        create(root, "repository.ftl");
        root = createServiceModel(root, serviceJavaPackage);
        create(root, "service.ftl");
        createSqlModel(root);
        create(root, "sql.ftl");
        createMapperModel(root);
        create(root, "mapper.ftl");
    }

    /**
     * 创建属性
     *
     * @param propertyType 属性数据类型
     * @param propertyName 属性名称
     * @param describe     属性描述
     * @param propertyList 实体类属性集合
     */
    private static void createProperty(PropertyType propertyType, String propertyName, String describe, List<Property>
            propertyList) {
        Property property = new Property();
        property.setJavaType(propertyType);
        property.setPropertyName(propertyName);
        property.setDescribe(describe);
        property.setUnderlinePropertyName(StringUtil.camelToUnderline(property.getPropertyName()));
        propertyList.add(property);
    }

    private static void create(Map<String, Object> root, String templateName) {
        Configuration cfg = new Configuration();

        try {
            // 步骤一：指定 模板文件从何处加载的数据源，这里设置一个文件目录
            cfg.setDirectoryForTemplateLoading(new File("./src/main/java/com/ipzoe/token75/freemarker/template"));
            cfg.setObjectWrapper(new DefaultObjectWrapper());

            // 步骤二：获取 模板文件
            Template template = cfg.getTemplate(templateName);

            // 步骤四：合并 模板 和 数据模型
            // 创建.java类文件
            if (javaFile != null) {
                Writer javaWriter = new FileWriter(javaFile);
                template.process(root, javaWriter);
                javaWriter.flush();
                System.out.println("文件生成路径：" + javaFile.getCanonicalPath());

                javaWriter.close();
            }
            // 输出到Console控制台
            Writer out = new OutputStreamWriter(System.out);
            template.process(root, out);
            out.flush();
            out.close();

        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建数据模型
     *
     * @param javaPackage  java类包名
     * @param className    实体类名(驼峰)
     * @param describe     实体类描述
     * @param propertyList 属性集合
     * @return 实体类模型
     */
    private static Map<String, Object> createDataModel(String javaPackage, String className, String describe,
                                                       List<Property> propertyList) {
        Map<String, Object> root = Maps.newHashMap();
        Entity entity = new Entity();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        entity.setCurrentDate(formatter.format(LocalDateTime.now()));

        // 创建包名
        entity.setJavaPackage(javaPackage);

        // 创建类名
        entity.setSuperclass("BaseEntity");
        entity.setClassName(className);
        entity.setName(StringUtil.initialCase(className));
        entity.setDescribe(describe);
        entity.setUnderlineName(StringUtil.camelToUnderline(entity.getName()));

        if (null != propertyList) {
            for (Property property : propertyList) {
                if (PropertyType.BigDecimal.equals(property.getJavaType())) {
                    entity.setHasBigDecimal(true);
                }
                if (PropertyType.LocalDateTime.equals(property.getJavaType())) {
                    entity.setHasLocalDateTime(true);
                }
                if (PropertyType.LocalDate.equals(property.getJavaType())) {
                    entity.setHasLocalDate(true);
                }
                if (PropertyType.LocalTime.equals(property.getJavaType())) {
                    entity.setHasLocalTime(true);
                }
            }
        }

        // 将属性集合添加到实体对象中
        entity.setProperties(propertyList);

        // 创建.java类文件
        File outDirFile = new File("./src/main/java/");
        if (!outDirFile.exists()) {
            outDirFile.mkdir();
        }

        javaFile = toJavaFilename(outDirFile, entity.getJavaPackage(), entity.getClassName());

        root.put("entity", entity);
        return root;
    }

    /**
     * 创建Repository模型
     *
     * @param root              实体类模型
     * @param repositoryPackage Repository包名
     * @return Repository模型
     */
    private static Map<String, Object> createRepositoryModel(Map<String, Object> root, String repositoryPackage) {
        Entity entity = (Entity) root.get("entity");
        entity.setRepositoryPackage(repositoryPackage);

        // 创建.java类文件
        File outDirFile = new File("./src/main/java/");
        if (!outDirFile.exists()) {
            outDirFile.mkdir();
        }
        javaFile = toJavaFilename(outDirFile, entity.getRepositoryPackage(), entity.getClassName() + "Repository");
        root.put("entity", entity);

        return root;
    }

    /**
     * 创建Service模型
     *
     * @param root           实体类模型
     * @param servicePackage Service包名
     * @return Service模型
     */
    private static Map<String, Object> createServiceModel(Map<String, Object> root, String servicePackage) {
        Entity entity = (Entity) root.get("entity");
        entity.setServicePackage(servicePackage);

        // 创建.java类文件
        File outDirFile = new File("./src/main/java/");
        if (!outDirFile.exists()) {
            outDirFile.mkdir();
        }
        javaFile = toJavaFilename(outDirFile, entity.getServicePackage(), entity.getClassName() + "Service");
        root.put("entity", entity);

        return root;
    }

    /**
     * 创建Sql模型
     *
     * @param root 实体类模型
     */
    private static void createSqlModel(Map<String, Object> root) {
        Entity entity = (Entity) root.get("entity");

        javaFile = toSqlFilename(entity.getUnderlineName());

    }

    /**
     * 创建Mapper模型
     *
     * @param root 实体类模型
     */
    private static void createMapperModel(Map<String, Object> root) {
        Entity entity = (Entity) root.get("entity");

        javaFile = toMapperFilename(entity.getClassName());

    }

    /**
     * 创建.java文件所在路径 和 返回.java文件File对象
     *
     * @param outDirFile    生成文件路径
     * @param javaPackage   java包名
     * @param javaClassName java类名
     * @return .java文件File对象
     */
    private static File toJavaFilename(File outDirFile, String javaPackage, String javaClassName) {
        String packageSubPath = javaPackage.replace('.', '/');
        File packagePath = new File(outDirFile, packageSubPath);
        File file = new File(packagePath, javaClassName + ".java");
        if (!packagePath.exists()) {
            packagePath.mkdirs();
        }
        return file;
    }

    /**
     * 创建.mapper文件所在路径 和 返回.mapper文件File对象
     *
     * @param javaName java类名(非驼峰)
     * @return .mapper文件File对象
     */
    private static File toMapperFilename(String javaName) {
        File packagePath = new File("./src/main/resources/", "mapper");
        File file = new File(packagePath, javaName + "Mapper.xml");
        if (!packagePath.exists()) {
            packagePath.mkdirs();
        }
        return file;
    }

    /**
     * 创建.sql文件所在路径 和 返回.sql文件File对象
     *
     * @param javaName java类名(非驼峰)
     * @return .sql文件File对象
     */
    private static File toSqlFilename(String javaName) {
        File packagePath = new File("./src/main/resources/", "sql");
        File file = new File(packagePath, javaName + ".sql");
        if (!packagePath.exists()) {
            packagePath.mkdirs();
        }
        return file;
    }
}
